import axios from "axios";
import { getToken, setToken, getRefreshToken } from "@utils/auth";

// cookie
// 本地存储方案有三种
// localStorage
// 存储时间限制 没有 除非手动删除
// 大小 5MB
// SessionStorage
// 存储时间限制 关闭页面会删除
// 大小 5MB
// Cookie
// 存储时间限制 可以手动设置到期时间  也可以设置https访问 等
// 大小 4kb
// 和localStorage与SessionStorage 的区别是  cookie可以和服务端做通信（每次发送接口的时候可以携带上cookie的参数）

// 刷新 access_token 的接口
// token 是有有效期的 一般是2h 或者24h 每个公司不一样
// 如果token过期的话  一般接口都会报 403 就需要重新的登录
// 一般公司要做无痛刷新的
// 什么是无痛刷新 假设token 2h过期， 用户每次发起接口 都判断一下token还要多久过期
// 如果过期时间只剩15分钟  则调用刷新token的接口 返回一个新的token替换上就可以了
// 如果用户2h内没有任何操作 则token过期

// 单点登录
// 只能在一个设备登录
// 我们如果账号被顶  会报 403错误  并且提示用户 账号在其他地方登录
// token会直接失效 然后重定向到login

const refreshToken = () => {
	return instance.post(
		"/auth/refresh",
		{ refresh_token: getRefreshToken() },
		true
	);
};

// 创建 axios 实例
const instance = axios.create({
	baseURL: process.env.GATSBY_API_URL, // 接口前缀
	timeout: 30000, // 接口超时时间
	headers: {
		"Content-Type": "application/json", // 请求报文格式
	},
});

let isRefreshing = false; // 标记是否正在刷新 token
let requests = []; // 存储待重发请求的数组
// 浏览器只能同时并发5个接口
// 如果页面同时发起接口过多 可能会卡
// 可以在请求拦截里 吧多余的接口暂存起来  分批执行
instance.interceptors.response.use(
	response => {
		return response;
	},
	error => {
		if (!error.response) {
			return Promise.reject(error);
		}
		if (
			error.response.status === 401 &&
			!error.config.url.includes("/auth/refresh")
		) {
			const { config } = error;
			if (!isRefreshing) {
				isRefreshing = true;
				return refreshToken()
					.then(res => {
						const { access_token } = res.data;
						setToken(access_token);
						config.headers.Authorization = `Bearer ${access_token}`;
						// token 刷新后将数组的方法重新执行
						requests.forEach(cb => cb(access_token));
						requests = []; // 重新请求完清空
						return instance(config);
					})
					.catch(err => {
						console.log("抱歉，您的登录状态已失效，请重新登录！");
						return Promise.reject(err);
					})
					.finally(() => {
						isRefreshing = false;
					});
			} else {
				// 返回未执行 resolve 的 Promise
				return new Promise(resolve => {
					// 用函数形式将 resolve 存入，等待刷新后再执行
					requests.push(token => {
						config.headers.Authorization = `Bearer ${token}`;
						resolve(instance(config));
					});
				});
			}
		}
		return Promise.reject(error);
	}
);

// 给请求头添加 access_token
const setHeaderToken = isNeedToken => {
	const accessToken = isNeedToken ? getToken() : null;
	if (isNeedToken) {
		// api 请求需要携带 access_token
		if (!accessToken) {
			console.log("不存在 access_token 则跳转回登录页");
		}
		instance.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
	}
};

// 有些 api 并不需要用户授权使用，则无需携带 access_token；默认不携带，需要传则设置第三个参数为 true
export const get = (url, params = {}, isNeedToken = false) => {
	setHeaderToken(isNeedToken);
	return instance({
		method: "get",
		url,
		params,
	});
};

export const post = (url, params = {}, isNeedToken = false) => {
	setHeaderToken(isNeedToken);
	return instance({
		method: "post",
		url,
		data: params,
	});
};
